/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.modules.vcs.cmdline;
import java.util.*;
import org.netbeans.modules.vcs.util.*;
import org.netbeans.modules.vcs.*;
/** Expand Bash style variables ${USER}.
*
* @author Michal Fadljevic, Pavel Buzek
*/
//-------------------------------------------
public class Variables {
private Debug E=new Debug("Variables", false); // NOI18N
private Debug D=E;
private boolean warnUndefVars = true;
private static final String SUBSTRACT = "-"; // NOI18N
private static final String REPLACE = "_"; // NOI18N
//-------------------------------------------
/** Expand all occurences of ${VARIABLE} repeatetively.
* It uses {@link #needFurtherExpansion} function to see
* when to stop scanning (via {@link expandOnce}).
* @param tab Hashtable holding (String)VARIABLE=(String)VALUE pairs
* @param cmd Command in which ${VAR} sequences
* @return String with all variables expanded
*/
public String expand(Hashtable tab, String cmd, boolean warnUndefVars) {
D.deb ("expand ("+tab+","+cmd+")"); // NOI18N
String cmd_cond = ""; // NOI18N
this.warnUndefVars = warnUndefVars;
boolean expanded = false;
do {
while (true) {
cmd_cond=expandConditional (tab,cmd);
if (cmd.equals(cmd_cond)) break;
cmd = cmd_cond;
}
expanded = true;
//D.deb ("after expandConditional ("+tab+","+cmd+")"); // NOI18N
while(needFurtherExpansion(cmd)==true){
//D.deb("needFurtherExpansion cmd='"+cmd+"'"); // NOI18N
cmd=expandOnce(tab,cmd);
expanded = false;
}
} while(!expanded);
D.deb ("after expansion ("+tab+","+cmd+")"); // NOI18N
return MiscStuff.replaceBackslashDollars( cmd );
}
/** Expand all occurences of ${VARIABLE} repeatetively.
* It makes only one expansion cycle.
* @param tab Hashtable holding (String)VARIABLE=(String)VALUE pairs
* @param cmd Command in which ${VAR} sequences
* @return String with all variables expanded
*/
public String expandFast(Hashtable tab, String cmd, boolean warnUndefVars) {
D.deb ("expandFast ("+cmd+")"); // NOI18N
String cmd_cond = ""; // NOI18N
this.warnUndefVars = warnUndefVars;
boolean expanded = false;
cmd_cond = expandConditional (tab,cmd);
cmd=expandOnce(tab,cmd_cond);
D.deb ("after expansion ("+cmd+")"); // NOI18N
return MiscStuff.replaceBackslashDollars( cmd );
}
private int getPairIndex(String str, int from, char p1, char p2) {
int len = str.length();
int cp = 1;
int i = from;
for(; i < len; i++) {
if (str.charAt(i) == p1) cp++;
if (str.charAt(i) == p2) cp--;
if (cp == 0) break;
}
if (i < len) return i;
else return -1;
}
public String expandConditional (Hashtable tab, String cmd){
//D.deb ("expandConditional (..)"); // NOI18N
int index=0;
int size=cmd.length();
int begin=0,end=0,nextBegin=0;
StringBuffer result=new StringBuffer(size+20);
while(true){
begin=cmd.indexOf("$[?",index); // NOI18N
//D.deb("begin="+begin); // NOI18N
if( begin<0 ){
result.append(cmd.substring(index));
break;
}
result.append(cmd.substring(index,begin));
//D.deb ("pre="+result); // NOI18N
int fake=cmd.indexOf("\\$[?",index); // NOI18N
if( fake<0 ){
// why 5 ? no one knows - magical number (<-1)
fake=-5;
}
if(fake+1==begin){
index=begin+1;
result.append('$');
continue;
}
//end=cmd.indexOf("]",begin); // NOI18N
end = getPairIndex(cmd, begin + 3, '[', ']');
if( end<0 ){
index=begin+1;
continue;
}
//D.deb("end="+end); // NOI18N
String var=cmd.substring(begin+3,end).trim();
String value=getVarValue(tab, var); //(String)tab.get(var);
//D.deb("var="+var+", value="+value); // NOI18N
if( value == null ){
if (warnUndefVars) {
E.err("Variable undefined '"+var+"'. Expanding it to an empty string."); // NOI18N
}
if( var.indexOf("$[?")>=0 ){ // NOI18N
E.err("Missing closing bracket ']' ?"); // NOI18N
}
}
index=end+1;
// find first and second option and choose
// first
int firstBegin=cmd.indexOf("[",index); // NOI18N
//D.deb("firstBegin="+firstBegin); // NOI18N
if( firstBegin<0 ){
result.append(cmd.substring(index));
break;
}
//result.append(cmd.substring(index,firstBegin));
fake=cmd.indexOf("\\[",index); // NOI18N
if( fake<0 ){
// why 5 ? no one knows - magical number (<-1)
fake=-5;
}
if(fake+1==firstBegin){
index=firstBegin+1;
result.append('[');
continue;
}
//int firstEnd=cmd.indexOf("]",firstBegin); // NOI18N
int firstEnd = getPairIndex(cmd, firstBegin + 1, '[', ']');
if( firstEnd<0 ){
index=firstBegin+1;
continue;
}
//D.deb("firstEnd="+firstEnd); // NOI18N
String first=cmd.substring(firstBegin+1,firstEnd);
//D.deb ("first="+first); // NOI18N
index=firstEnd;
// second
int secondBegin=cmd.indexOf("[",index); // NOI18N
//D.deb("secondBegin="+secondBegin); // NOI18N
if( secondBegin<0 ){
result.append(cmd.substring(index));
break;
}
//result.append(cmd.substring(index,secondBegin));
fake=cmd.indexOf("\\[",index); // NOI18N
if( fake<0 ){
// why 5 ? no one knows - magical number (<-1)
fake=-5;
}
if(fake+1==secondBegin){
index=secondBegin+1;
result.append('[');
continue;
}
//int secondEnd=cmd.indexOf("]",secondBegin); // NOI18N
int secondEnd = getPairIndex(cmd, secondBegin + 1, '[', ']');
if( secondEnd<0 ){
index=secondBegin+1;
continue;
}
//D.deb("secondEnd="+secondEnd); // NOI18N
String second=cmd.substring(secondBegin+1,secondEnd);
//D.deb ("second="+second); // NOI18N
index = secondEnd+1;
if(value==null || value.equals ("")) { // NOI18N
result.append (/*"["+*/second); // NOI18N
} else {
result.append (/*"["+*/first); // NOI18N
}
}
return new String(result);
}
//-------------------------------------------
public boolean needFurtherExpansion(String cmd){
//D.deb ("needFurtherExpansion("+cmd+")"); // NOI18N
int begin=cmd.indexOf("${"); // NOI18N
int fake=cmd.indexOf("\\${"); // NOI18N
if( begin<0 ){
return false ;
}
if( fake<0 ){
fake=-5;
}
//D.deb("begin="+begin+", fake="+fake); // NOI18N
if(fake+1==begin){
return needFurtherExpansion(cmd.substring(begin+1));
}
return true ;
}
//-------------------------------------------
/** Expand (once) ${VARIABLE} variables in command.
* It scans 'cmd' string and replaces all occurences of ${VARIABLE}
* to VALUE=tab.get(VARIABLE). Both VARIABLE and VALUE must be Strings.
* <p>
* Note that 'cmd' string is scanned only once.
* Use {@link #needFurtherExpansion} function to see if it should
* be called again.
* @param tab Hashtable holding (String)VARIABLE=(String)VALUE pairs
* @param cmd Command in which ${VAR} sequences
* @return String with all variables expanded
*/
public String expandOnce(Hashtable tab, String cmd){
//D.deb ("expandOnce (..)"); // NOI18N
int index=0;
int size=cmd.length();
int begin=0,end=0,nextBegin=0;
StringBuffer result=new StringBuffer(size+20);
while(true){
begin=cmd.indexOf("${",index); // NOI18N
//D.deb("begin="+begin); // NOI18N
if( begin<0 ){
result.append(cmd.substring(index));
break;
}
result.append(cmd.substring(index,begin));
int fake=cmd.indexOf("\\${",index); // NOI18N
if( fake<0 ){
fake=-5;
}
if(fake+1==begin){
index=begin+1;
result.append('$');
continue;
}
end=cmd.indexOf("}",begin); // NOI18N
if( end<0 ){
index=begin+1;
continue;
}
//D.deb("end="+end); // NOI18N
String var=cmd.substring(begin+2,end);
String value=getVarValue(tab, var);
//String value=(String)tab.get(var);
//D.deb("var="+var+", value="+value); // NOI18N
if( value != null ){
result.append(value);
} else {
if (warnUndefVars) {
E.err("Variable undefined '"+var+"'. Expanding it to an empty string."); // NOI18N
}
if( var.indexOf("${")>=0 ){ // NOI18N
E.err("Missing closing bracket '}' ?"); // NOI18N
}
}
index=end+1;
}
return new String(result);
}
/**
* Get the value of a variable. If the variable name is not known,
* this method search for the last occurence of character '_' and if it
* is followed by two more chracters it replaces the first by the second
* in the value of that variable.
* @param tab The table holding (String)VARIABLE=(String)VALUE pairs
* @param name The variable name or expression to evaluate
*/
private String getReplaceVarValue(Hashtable tab, String name) {
if (name == null) return null;
String value = (String) tab.get(name);
if (value == null) {
int r = name.lastIndexOf(REPLACE);
//D.deb("getReplaceVarValue('"+name+"'): r = "+r); // NOI18N
if (r > 0) {
value = (String) tab.get(name.substring(0, r));
//D.deb("getReplaceVarValue(): value of '"+name.substring(0, r)+"' = '"+value+"'"); // NOI18N
//D.deb("length = "+name.length()); // NOI18N
if (value != null && name.length() >= r+3) {
char c1 = name.charAt(r+1);
char c2 = name.charAt(r+2);
//D.deb("c1 = "+c1+", c2 = "+c2); // NOI18N
value = value.replace(c1, c2);
}
}
}
return value;
}
/**
* Get the value of a variable or an expression.
* @param tab The table holding (String)VARIABLE=(String)VALUE pairs
* @param name The variable name or expression to evaluate
*/
private String getVarValue(Hashtable tab, String name) {
int substr;
int begin = 0;
String value = getReplaceVarValue(tab, name);
if (value == null) {
while((substr = name.indexOf(SUBSTRACT, begin)) > 0) {
String var = name.substring(begin, substr);
if (var != null) var = var.trim();
String svalue = getReplaceVarValue(tab, var);
if (svalue == null) {
if (warnUndefVars) E.deb("Variable undefined '"+var+"'."); // NOI18N
return null;
}
if (begin == 0) value = svalue;
else value = VcsFileSystem.substractRootDir(value, svalue);
begin += substr + SUBSTRACT.length();
}
if (begin == 0) value = (String) tab.get(name);
else value = VcsFileSystem.substractRootDir(value, getReplaceVarValue(tab, name.substring(begin).trim()));
}
return value;
}
//-------------------------------------------
public static void main(String[] args){
Hashtable vars=new Hashtable();
vars.put("A","a"); // NOI18N
vars.put("B","${A}b"); // NOI18N
vars.put("BB","\\${A}b"); // NOI18N
vars.put("C","${B}c"); // NOI18N
vars.put("CC","\\${A}\\${B}c"); // NOI18N
vars.put("DIR","src"); // NOI18N
vars.put("STCMD","stcmd30"); // NOI18N
Variables v=new Variables();
System.out.println("vars="+vars); // NOI18N
System.out.println("orig='"+args[0]+"'"); // NOI18N
System.out.println("new ='"+v.expand(vars,args[0], true)+"'"); // NOI18N
}
}
/*
* Log
* 15 Gandalf-post-FCS1.13.2.0 3/23/00 Martin Entlicher A simple fast variable
* expansion added.
* 14 Gandalf 1.13 1/18/00 Martin Entlicher Warning of undefined
* variables on demand.
* 13 Gandalf 1.12 1/15/00 Ian Formanek NOI18N
* 12 Gandalf 1.11 1/6/00 Martin Entlicher
* 11 Gandalf 1.10 12/21/99 Martin Entlicher
* 10 Gandalf 1.9 12/8/99 Martin Entlicher Added deeper expansion
* of commands.
* 9 Gandalf 1.8 12/8/99 Martin Entlicher Added substract and
* replace to variables.
* 8 Gandalf 1.7 10/25/99 Pavel Buzek
* 7 Gandalf 1.6 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 6 Gandalf 1.5 10/13/99 Pavel Buzek
* 5 Gandalf 1.4 10/13/99 Martin Entlicher Recursive command
* expansion added
* 4 Gandalf 1.3 10/12/99 Martin Entlicher
* 3 Gandalf 1.2 10/12/99 Pavel Buzek
* 2 Gandalf 1.1 10/5/99 Pavel Buzek VCS at least can be
* mounted
* 1 Gandalf 1.0 9/30/99 Pavel Buzek
* $
*/